package cn.lingyangwl.agile.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.lingyangwl.agile.model.TreeSelectResp;
import cn.lingyangwl.agile.model.constants.TreeCons;
import cn.lingyangwl.agile.model.enums.BoolEnum;
import cn.lingyangwl.agile.system.assembly.SysAreaAssembly;
import cn.lingyangwl.agile.system.mapper.SysAreaMapper;
import cn.lingyangwl.agile.system.model.entity.SysArea;
import cn.lingyangwl.agile.system.model.rqrs.area.*;
import cn.lingyangwl.agile.system.service.SysAreaService;
import cn.lingyangwl.framework.mybatis.plus.utils.PageUtil;
import cn.lingyangwl.framework.tool.core.CollectionUtils;
import cn.lingyangwl.framework.tool.core.StringUtils;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import cn.lingyangwl.framework.tool.core.tree.TreeUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 行政区划
 *
 * @author shenguangyang
 */
@Slf4j
@Service
public class SysAreaServiceImpl extends ServiceImpl<SysAreaMapper, SysArea>
    implements IService<SysArea>, SysAreaService {

    @Resource
    private SysAreaAssembly areaAssembly;
    
    @Override
    public IPage<AreaResp> pageArea(AreaPageReq req) {
        Integer pageNum = req.getPageNum();
        LambdaQueryWrapper<SysArea> lqw = SysArea.lqw()
            .like(StringUtils.isNotEmpty(req.getAreaName()), SysArea::getAreaName, req.getAreaName())
            .eq(StringUtils.isNotEmpty(req.getParentId()), SysArea::getParentId, req.getParentId())
            .eq(StringUtils.isNotEmpty(req.getAreaId()), SysArea::getId, req.getAreaId())
            .eq(StringUtils.isNotEmpty(req.getStatus()), SysArea::getStatus, req.getStatus())
            .orderByAsc(SysArea::getId);

        // 前端触发了懒加载, 查找某个父级的所有一级子节点
        if (Objects.isNull(pageNum)) {
            List<AreaResp> respList = this.list(lqw).stream().map(areaAssembly::toAreaResp).collect(Collectors.toList());
            IPage<AreaResp> page = new Page<>();
            page.setRecords(respList);
            return page;
        }
        int pageSize = Math.max(1, req.getPageSize());
        IPage<SysArea> pageData = this.page(new Page<>(pageNum, pageSize), lqw);
        return PageUtil.to(pageData, areaAssembly::toAreaResp);
    }

    @Override
    public List<AreaResp> listArea(AreaListReq req) {
        LambdaQueryWrapper<SysArea> lqw = SysArea.lqw()
                .like(StringUtils.isNotEmpty(req.getAreaName()), SysArea::getAreaName, req.getAreaName())
                .eq(StringUtils.isNotEmpty(req.getParentId()), SysArea::getParentId, req.getParentId())
                .orderByAsc(SysArea::getId);
        return this.list(lqw).stream().map(areaAssembly::toAreaResp).collect(Collectors.toList());
    }

    @Override
    public List<TreeSelectResp> listTreeSelect(AreaListReq req) {
        LambdaQueryWrapper<SysArea> lqw = SysArea.lqw()
                .like(StringUtils.isNotEmpty(req.getAreaName()), SysArea::getAreaName, req.getAreaName())
                .orderByAsc(SysArea::getId);

        List<String> disabledIds = new ArrayList<>();
        List<TreeSelectResp> flatList = this.list(lqw).stream().map(e -> {
            TreeSelectResp resp = areaAssembly.toAreaTreeSelect(e);
            if ((StringUtils.isNotEmpty(req.getExcludeId()) && e.getId().equals(req.getExcludeId()))
                    || BoolEnum.FALSE.getCode().equals(e.getStatus())) {
                resp.setDisabled(true);
                disabledIds.add(resp.getId());
            }
            return resp;
        }).collect(Collectors.toList());

        // 如果父级被禁用, 所有的孩子也被禁用
        TreeUtils.listFlatChild(flatList, disabledIds, node -> node.setDisabled(true));
        return req.getIsBuildTree() ? TreeUtils.build(flatList, TreeCons.ROOT_ANCESTORS_ID) : flatList;
    }

    @Override
    public AreaResp getArea(String areaId) {
        SysArea entity = this.getOne(SysArea.lqw().eq(SysArea::getId, areaId));
    	return areaAssembly.toAreaResp(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveArea(AreaSaveReq req) {
        SysArea entity = areaAssembly.toArea(req);
        entity.setTreeLeaf(SysArea.TreeLeaf.YES);

        if (this.getArea(req.getId()) != null) {
            throw new BizException("区域编码已存在");
        }

        // 根节点
        if (StringUtils.isEmpty(req.getParentId()) || TreeCons.ROOT_ANCESTORS_ID.equals(req.getParentId())) {
            entity.setParentId(TreeCons.ROOT_ANCESTORS_ID);
            entity.setParentIds(TreeCons.ROOT_ANCESTORS_ID);
            entity.setTreeLevel(0);
            entity.setSortNo(0);
            entity.setTreeNames(req.getAreaName());
        } else {
            SysArea preParentArea = this.getOne(SysArea.lqw().eq(SysArea::getId, req.getParentId()));
            if (Objects.isNull(preParentArea)) {
                throw new BizException("父级区域不存在");
            }

            // 查询所有父亲数据
            LambdaQueryWrapper<SysArea> allLqw = SysArea.lqw();
            List<SysArea> areaList = this.list(allLqw);
            Map<String, SysArea> allAreaMap = areaList.stream().collect(Collectors.toMap(SysArea::getId, Function.identity()));
            LinkedList<String> parentCodes = new LinkedList<>();

            // 获取父节点名称并以斜杠划分, eg: 北京市/北京城区/东城区
            String nextFindId = preParentArea.getId();
            LinkedList<String> treeNames = new LinkedList<>();
            LinkedList<SysArea> parentList = new LinkedList<>();
            while (!TreeCons.ROOT_ANCESTORS_ID.equals(nextFindId) && allAreaMap.containsKey(nextFindId)) {
                SysArea area = allAreaMap.get(nextFindId);
                parentCodes.addFirst(area.getId());
                treeNames.addFirst(area.getAreaName());
                parentList.addFirst(area);
                nextFindId = area.getParentIds();
            }
            treeNames.addLast(req.getAreaName());

            entity.setTreeLevel(treeNames.size() - 1);
            entity.setTreeNames(String.join("/", treeNames));
            entity.setParentIds(String.join(",", parentCodes) + ",");

            // 更新所有父节点
            parentList.forEach(e -> e.setTreeLeaf(SysArea.TreeLeaf.NO));
            this.updateBatchById(parentList);
        }

        return this.save(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateArea(AreaUpdateReq req) {
        SysArea entity = areaAssembly.toArea(req);

        SysArea oldArea = this.getOne(SysArea.lqw().eq(SysArea::getId, req.getId()));
        if (Objects.isNull(oldArea)) {
            throw new BizException("区域不存在");
        }
        if (req.getId().equals(req.getParentId())) {
            throw new BizException("上级区域不能选自己");
        }

        // 查找全部数据
        List<SysArea> areaList = this.list();
        Map<String, SysArea> allAreaMap = areaList.stream().collect(Collectors.toMap(SysArea::getId, Function.identity()));

        // 存放新的和旧的节点id
        String nextFindId = oldArea.getId();
        Set<String> areaIds = listAllParentIds(oldArea, allAreaMap);

        entity.setSortNo(ObjectUtil.defaultIfNull(req.getSortNo(), Integer.parseInt(req.getId())));
        entity.setAreaName(entity.getAreaName());
        entity.setAreaType(entity.getAreaType());
        entity.setStatus(entity.getStatus());
        this.updateById(entity);

        // 2. 查找新区域树的所有节点id
        SysArea currArea = this.getOne(SysArea.lqw().eq(SysArea::getId, req.getId()));
        nextFindId = currArea.getId();
        while (!TreeCons.ROOT_ANCESTORS_ID.equals(nextFindId) && allAreaMap.containsKey(nextFindId)) {
            SysArea area = allAreaMap.get(nextFindId);
            areaIds.add(area.getId());
            nextFindId = area.getParentId();
        }

        List<SysArea> updatedAreaList = this.list(SysArea.lqw().in(SysArea::getParentId, areaIds));

        List<AreaTree> flatTreeList = updatedAreaList.stream().map(areaAssembly::toAreaTree).collect(Collectors.toList());
        List<AreaTree> treeList = TreeUtils.build(flatTreeList, TreeCons.ROOT_ANCESTORS_ID);

        return this.updateBatchById(TreeUtils.flatTree(treeList).stream().map(areaAssembly::toArea).collect(Collectors.toList()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteArea(String areaId) {
        SysArea currArea = this.getOne(SysArea.lqw().eq(SysArea::getId, areaId));
        if (Objects.isNull(currArea)) {
            throw new BizException("删除的区域不存在");
        }
        List<String> deletedCodes = this.baseMapper.listChildArea(areaId).stream().map(SysArea::getId).collect(Collectors.toList());
        deletedCodes.add(areaId);
        this.removeByIds(deletedCodes);

        // 排除根节点
        if (TreeCons.ROOT_ANCESTORS_ID.equals(currArea.getParentId())) {
            return true;
        }
        // 或者删除的几点父级
        SysArea parentArea = this.getOne(SysArea.lqw().eq(SysArea::getId, currArea.getParentId()));

        // 查找全部数据
        List<SysArea> areaList = this.list();
        Map<String, SysArea> allAreaMap = areaList.stream().collect(Collectors.toMap(SysArea::getId, Function.identity()));

        Set<String> areaIds = listAllParentIds(parentArea, allAreaMap);

        if (CollectionUtils.isEmpty(areaIds)) {
            return true;
        }
        List<AreaTree> flatTreeList = this.list(SysArea.lqw().in(SysArea::getId, areaIds))
                .stream().map(areaAssembly::toAreaTree).collect(Collectors.toList());
        List<AreaTree> treeList = TreeUtils.build(flatTreeList, TreeCons.ROOT_ANCESTORS_ID);

        return this.updateBatchById(TreeUtils.flatTree(treeList).stream().map(areaAssembly::toArea).collect(Collectors.toList()));
    }

    @Override
    public void updateStatus(String id, String status) {
        SysArea area = new SysArea();
        area.setId(id);
        area.setStatus(status);
        this.updateById(area);
    }

    private Set<String> listAllParentIds(SysArea preParentArea, Map<String, SysArea> allAreaMap) {
        // 1. 查找旧的树的所有节点id
        Set<String> areaIds = new HashSet<>();
        String nextFindId = preParentArea.getId();
        while (!TreeCons.ROOT_ANCESTORS_ID.equals(nextFindId) && allAreaMap.containsKey(nextFindId)) {
            SysArea area = allAreaMap.get(nextFindId);
            areaIds.add(area.getId());
            nextFindId = area.getParentId();
        }
        return areaIds;
    }


    @Override
    public List<SysArea> listAreaByNames(List<String> areaTypes, List<String> areaNames) {
        if (CollUtil.isEmpty(areaTypes) && CollUtil.isEmpty(areaNames)) {
            throw new BizException("区域类型和区域名称不能同时为空");
        }
        LambdaQueryWrapper<SysArea> lqw = SysArea.lqw()
            .in(CollUtil.isNotEmpty(areaTypes),SysArea::getAreaType, areaTypes)
            .in(CollUtil.isNotEmpty(areaNames), SysArea::getAreaName, areaNames);
        return this.list(lqw);
    }
}
